<!--弹窗组件-->
<template>
  <AntSpin style="position:absolute;top:0px;">
    <a-modal
      ref="cmodal"
      :width="width"
      :bodyStyle="bodyStyle"
      :visible="cvisible"
      :dialogStyle="dialogStyle"
      :maskClosable="maskClosable"
      :destroyOnClose="true"
      :footer="footer"
      @cancel="cancel"
    >
      <template slot="title">
        <div ref="modalTitleRef" :class="{'title-drag':true}" @mousedown="(e)=>modalMouseDown(e)">
          <slot name="title">{{ title }}</slot>
        </div>
      </template>
      <div slot="footer">
        <div class="c-modal-footer">
          <slot name="footer"></slot>
        </div>
      </div>
      <div class="c-modal-container" :style="{width:windowWidth+'px'}">
        <div class="c-modal-container-body">
          <div ref="modalBody">
            <slot></slot>
          </div>
        </div>
      </div>
    </a-modal>
  </AntSpin>
</template>

<script>
export default {
  name: 'AntModal',
  props: {
    /**
     * 弹窗标题
     */
    title: {
      type: String,
      default: '弹窗'
    },
    /**
     * 是否显示弹窗
     */
    visible: {
      type: Boolean,
      default: false
    },
    /**
     * 底部内容，当不需要默认底部按钮时，可以设为 :footer="null" string|slot	确定取消按钮
     */
    footer: {
      type: String
    },
    /**
     * 点击蒙层是否允许关闭
     */
    maskClosable: {
      type: Boolean,
      default: false
    },
    /**
     * 可用于设置浮层的样式，调整浮层位置等
     */
    dialogStyle: {
      type: Object,
      default: function() {
        return { }
      }
    }
  },
  data() {
    return {
      width: document.body.clientWidth,
      // 距离顶部、底部距离
      topDistance: 50,
      cvisible: this.visible,
      // 弹出框内容区域高度值
      modalBodyHeight: null,
      // 弹出框除内容区域外的高度值
      modalOtherHeight: null,
      // modal移动样式 临时记录 对象
      modalStyle: {
        left: 0,
        top: 0
      },
      // 监听器
      rootRo: null
    }
  },
  computed: {
    /**
     * 窗口最大可是宽度
     * @returns {number}
     */
    windowWidth() {
      return document.body.clientWidth - 200
    },
    /**
     * 弹出框内容区域高度
     * @returns {{height: (string|string)}}
     */
    bodyStyle() {
      return { height: this.modalBodyHeight ? this.modalBodyHeight + 'px' : 'auto' }
    }
  },
  mounted() {
  },
  beforeDestroy() {},
  methods: {
    /**
     * 点击遮罩层或右上角叉或取消按钮的回调
     */
    cancel() {
      if (this.$listeners.cancel) {
        this.$emit('cancel')
      } else {
        this.cvisible = false
      }
      // 回调 - 弹窗关闭
      this.$emit('closeModal')
      this.closeModal()
    },
    /**
     * 关闭弹框 - 初始化参数
     */
    closeModal() {
      const modalBody = this.$refs.modalBody
      setTimeout(() => {
        modalBody.style.maxHeight = null
        this.modalBodyHeight = null
        this.modalOtherHeight = null
      }, 300)
      if (this.rootRo) {
        this.rootRo.disconnect()
        this.rootRo = null
      }
    },
    /**
     * 是否显示弹窗
     * @param visible true false
     */
    setVisible(visible) {
      this.resetModalStyle()
      this.cvisible = visible
      if (visible) {
        this.$nextTick(() => {
          const modalBody = this.$refs.modalBody
          if (modalBody) {
            this.autoAdaption(modalBody)
            this.observe(modalBody)
          }
        })
      } else {
        this.closeModal()
      }
    },
    /**
     * 监听
     * @param modalBody
     */
    observe(modalBody) {
      // 监听容器大小
      const _this = this
      if (this.rootRo) {
        this.rootRo.disconnect()
        this.rootRo = null
      }
      this.rootRo = new ResizeObserver(entries => {
        _this.autoAdaption(modalBody)
      })
      this.rootRo.observe(modalBody)
    },
    /**
     * 弹出框自适应
     * @param modalBody
     */
    autoAdaption(modalBody) {
      const maxHeight = this.getMaxHeight()
      const domScrollHeight = modalBody.scrollHeight + 1 // 判断存在滚动条情况下的高度，+1 做容错处理，保证页面效果
      let height = maxHeight >= domScrollHeight ? domScrollHeight : maxHeight
      height = height > this.topDistance ? height : this.topDistance
      this.$set(this, 'modalBodyHeight', height)
      modalBody.parentNode.style.maxHeight = height + 'px'
      let width = this.getDomWidth(modalBody) + (maxHeight >= domScrollHeight ? 0 : 8)
      // 判断弹框显示区域是否超出屏幕、超出则自行缩减
      if (width > this.windowWidth) {
        width = this.windowWidth
        modalBody.parentNode.style.maxWidth = width + 'px'
      }
      this.$set(this, 'width', width)
    },
    /**
     * 获取可视区域高度最大值
     * @returns {number}
     */
    getMaxHeight() {
      const maxHeight = document.body.clientHeight - 2 * this.topDistance
      if (this.modalOtherHeight == null) {
        let modalOtherHeight = 0
        const content = this.$refs.cmodal.$el
        // 获取头部、脚部区域
        const headers = document.getElementsByClassName('ant-modal-header')
        const footers = document.getElementsByClassName('ant-modal-footer')
        if (headers && headers.length > 0) {
          modalOtherHeight += headers[0].getBoundingClientRect().height
        }
        if (footers && footers.length > 0) {
          modalOtherHeight += footers[0].getBoundingClientRect().height
        }
        this.modalOtherHeight = modalOtherHeight
      }
      return maxHeight - this.modalOtherHeight
    },
    /**
     * 获取元素实际高度
     * @param dom
     * @returns {number}
     */
    getDomWidth(dom) {
      let oStyle = dom.currentStyle ? dom.currentStyle : window.getComputedStyle(dom, null)
      return parseFloat(oStyle.width)
    },
    /**
     * 重置弹窗样式
     */
    resetModalStyle() {
      this.modalStyle = { left: 0, top: 0 }
    },
    /**
     * 鼠标点击事件触发=》主要兼容鼠标移动-拖拽
     * @param e
     */
    modalMouseDown(e) {
      if (e.ctrlKey) {
        return
      }
      const _this = this
      _this.composedPath(e)
      // 记录鼠标在iframe或浏览器上的位置
      const posX = e.clientX
      const posY = e.offsetY
      const modalStyle = { ...this.modalStyle }
      const modalDom = e.path.find(el => el.className === 'ant-modal')
      document.onmousemove = function (e) {
        e.preventDefault()
        _this.composedPath(e)
        // 鼠标移动，用当前位置-鼠标点击时位置=》获取最新座标
        const left = e.clientX - posX
        const top = e.clientY - posY - 8

        modalDom.style.left = _this.modalStyle.left + left + 'px'
        modalDom.style.top = top + 'px'
        modalStyle.left = _this.modalStyle.left + left
        modalStyle.top = top
      }
      document.onmouseup = function (e) {
        e.preventDefault()
        _this.composedPath(e)
        document.onmousemove = null
        document.onmouseup = null
        _this.modalStyle = modalStyle
      }
    },
    /**
     * 冒泡 兼容处理 - 兼容事件没有path属性 - 递归寻找其所有上层dom
     * @param e
     * @returns {[]}
     */
    composedPath (e) {
      // 存在则直接return
      if (e.path) { return e.path }
      // 不存在则遍历target节点
      let target = e.target
      e.path = []
      while (target.parentNode !== null) {
        e.path.push(target)
        target = target.parentNode
      }
      // 最后补上document和window
      e.path.push(document, window)
      return e.path
    }
  }
}
</script>

<style lang="less" scoped>
::v-deep .ant-modal-body {
  padding:0px;
}
::v-deep .ant-modal-close-x{
  width: 43px;
  height: 43px;
  line-height: 43px;
}
::v-deep .ant-modal-header {
  padding: 8px 15px 8px 10px;
}
::v-deep .ant-modal-footer{
  padding: 8px 16px;
}

::v-deep .ant-modal {
  top: 10%;
}
::v-deep .ant-modal-wrap {
  overflow-x: hidden;
}
.title-drag{
  width: 100%;
  cursor: move;
}
.c-modal-footer{
  ::v-deep .c-button{
    margin:0px 0px 0px 16px;
  }
}
.c-modal-container{
  position: relative;
  .c-modal-container-body{
    position: absolute;
    overflow:auto;
  }
}
</style>
